<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>开发文档 | LinCMS-TP5入门</title>
    <meta name="generator" content="VuePress 1.5.0">
    
    <meta name="description" content="基于ThinkPHP 5.1的 Lin CMS 后端实现。">
    <link rel="preload" href="/lin-cms-book/assets/css/0.styles.e8c0fe6e.css" as="style"><link rel="preload" href="/lin-cms-book/assets/js/app.43f3ff7b.js" as="script"><link rel="preload" href="/lin-cms-book/assets/js/2.1c264783.js" as="script"><link rel="preload" href="/lin-cms-book/assets/js/3.12364cb0.js" as="script"><link rel="prefetch" href="/lin-cms-book/assets/js/10.b29049a1.js"><link rel="prefetch" href="/lin-cms-book/assets/js/11.5fc2ad3e.js"><link rel="prefetch" href="/lin-cms-book/assets/js/12.0fb72fc4.js"><link rel="prefetch" href="/lin-cms-book/assets/js/4.606d0aff.js"><link rel="prefetch" href="/lin-cms-book/assets/js/5.7bb207a9.js"><link rel="prefetch" href="/lin-cms-book/assets/js/6.1906fec0.js"><link rel="prefetch" href="/lin-cms-book/assets/js/7.2d983749.js"><link rel="prefetch" href="/lin-cms-book/assets/js/8.03718c42.js"><link rel="prefetch" href="/lin-cms-book/assets/js/9.2e1a8f4e.js">
    <link rel="stylesheet" href="/lin-cms-book/assets/css/0.styles.e8c0fe6e.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/lin-cms-book/" class="home-link router-link-active"><!----> <span class="site-name">LinCMS-TP5入门</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/lin-cms-book/" class="nav-link">
  简介
</a></div><div class="nav-item"><a href="/lin-cms-book/start/server_deploy.html" class="nav-link">
  入门
</a></div><div class="nav-item"><a href="/lin-cms-book/dev/" aria-current="page" class="nav-link router-link-exact-active router-link-active">
  开发文档
</a></div><div class="nav-item"><a href="/lin-cms-book/log/" class="nav-link">
  版本日志
</a></div><div class="nav-item"><a href="/lin-cms-book/qa/" class="nav-link">
  常见问题
</a></div><div class="nav-item"><a href="http://doc.cms.7yue.pro/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  林间有风
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div><div class="nav-item"><a href="https://github.com/ChenJinchuang/lin-cms-tp5" target="_blank" rel="noopener noreferrer" class="nav-link external">
  GitHub
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/lin-cms-book/" class="nav-link">
  简介
</a></div><div class="nav-item"><a href="/lin-cms-book/start/server_deploy.html" class="nav-link">
  入门
</a></div><div class="nav-item"><a href="/lin-cms-book/dev/" aria-current="page" class="nav-link router-link-exact-active router-link-active">
  开发文档
</a></div><div class="nav-item"><a href="/lin-cms-book/log/" class="nav-link">
  版本日志
</a></div><div class="nav-item"><a href="/lin-cms-book/qa/" class="nav-link">
  常见问题
</a></div><div class="nav-item"><a href="http://doc.cms.7yue.pro/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  林间有风
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div><div class="nav-item"><a href="https://github.com/ChenJinchuang/lin-cms-tp5" target="_blank" rel="noopener noreferrer" class="nav-link external">
  GitHub
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div> <!----></nav>  <ul class="sidebar-links"><li><a href="/lin-cms-book/dev/" aria-current="page" class="active sidebar-link">开发文档</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/lin-cms-book/dev/#项目结构" class="sidebar-link">项目结构</a></li><li class="sidebar-sub-header"><a href="/lin-cms-book/dev/#api规范" class="sidebar-link">API规范</a></li><li class="sidebar-sub-header"><a href="/lin-cms-book/dev/#数据库模型规范" class="sidebar-link">数据库模型规范</a></li><li class="sidebar-sub-header"><a href="/lin-cms-book/dev/#路由" class="sidebar-link">路由</a></li><li class="sidebar-sub-header"><a href="/lin-cms-book/dev/#验证器" class="sidebar-link">验证器</a></li><li class="sidebar-sub-header"><a href="/lin-cms-book/dev/#模型管理和权限管理" class="sidebar-link">模型管理和权限管理</a></li><li class="sidebar-sub-header"><a href="/lin-cms-book/dev/#行为日志" class="sidebar-link">行为日志</a></li><li class="sidebar-sub-header"><a href="/lin-cms-book/dev/#配置" class="sidebar-link">配置</a></li><li class="sidebar-sub-header"><a href="/lin-cms-book/dev/#令牌" class="sidebar-link">令牌</a></li></ul></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="开发文档"><a href="#开发文档" class="header-anchor">#</a> 开发文档</h1> <h2 id="项目结构"><a href="#项目结构" class="header-anchor">#</a> 项目结构</h2> <blockquote><p>LinCMS-TP5的目录结构遵循了ThinkPHP官方原生的tp5.1结构，如果你使用过TP5.0后者TP5.1那么对这个目录结构会很熟悉。<br>
如果你没接触过那也不要紧，可以参考下面的示例或者查看<a href="https://www.kancloud.cn/manual/thinkphp5_1/353946" target="_blank" rel="noopener noreferrer">ThinkPHP5.1完全开发手册<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>了解更多</p></blockquote> <div class="language-bash line-numbers-mode"><pre class="language-bash"><code>www  WEB部署目录（或者子目录）
├─application           应用目录
│  ├─common             公共模块目录（可以更改）
│  ├─api                模块目录
│  │  ├─common.php      模块函数文件
│  │  ├─controller      控制器目录
│  │  │  ├─cms          开发CMS API目录
│  │  │  └─v1           开发普通API目录
│  │  ├─model           模型目录
│  │  ├─validate        验证器目录
│  │  ├─config          配置目录
│  │  └─ <span class="token punctuation">..</span>.            更多类库目录
│  │
│  ├─command.php        命令行定义文件
│  ├─common.php         公共函数文件
│  └─tags.php           应用行为扩展定义文件
│
├─config                应用配置目录
│  ├─module_name        模块配置目录
│  │  ├─database.php    数据库配置
│  │  ├─cache           缓存配置
│  │  └─ <span class="token punctuation">..</span>.
│  │
│  ├─app.php            应用配置
│  ├─cache.php          缓存配置
│  ├─cookie.php         Cookie配置
│  ├─database.php       数据库配置
│  ├─log.php            日志配置
│  ├─session.php        Session配置
│  ├─template.php       模板引擎配置
│  └─trace.php          Trace配置
│
├─route                 路由定义目录
│  ├─route.php          路由定义
│  └─<span class="token punctuation">..</span>.                更多
│
├─public                WEB目录（对外访问目录）
│  ├─index.php          入口文件
│  ├─router.php         快速测试文件
│  └─.htaccess          用于apache的重写
│
├─thinkphp              框架系统目录
│  ├─lang               语言文件目录
│  ├─library            框架类库目录
│  │  ├─think           Think类库包目录
│  │  └─traits          系统Trait目录
│  │
│  ├─tpl                系统模板目录
│  ├─base.php           基础定义文件
│  ├─convention.php     框架惯例配置文件
│  ├─helper.php         助手函数文件
│  └─logo.png           框架LOGO文件
│
├─extend                扩展类库目录
├─runtime               应用的运行时目录（可写，可定制）
├─vendor                第三方类库目录（Composer依赖库）
├─build.php             自动生成定义文件（参考）
├─composer.json         composer 定义文件
├─LICENSE.txt           授权说明文件
├─README.md             README 文件
├─think                 命令行入口文件
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br><span class="line-number">60</span><br><span class="line-number">61</span><br></div></div><p>上面是项目的整体结构，开发时我们强烈建议你遵循如下规范开发，在前期你肯 定会不适应，但慢慢地你会爱上它。</p> <ul><li>在<code>application/api/controller</code>文件夹中开发<code>API</code>，并将不同版本，不同类型的<code>API</code>分开，如：v1 代表 第一版本的 API，v2 代表第二版本，cms 代表属于 cms 的 API。</li> <li>将程序的配置文件放在<code>config</code>文件夹下，并着重区分<code>secure（安全性配置)</code>和<code>setting（普通性配置）</code>。配置更详细内容参考<a href="#%E9%85%8D%E7%BD%AE">配置</a></li> <li>将可重用的类库放在<code>application/lib</code>文件夹下。</li> <li>将数据模型放在<code>application/api/model</code>文件夹下。</li> <li>将校验类放在<code>application/api/validate</code>文件夹下。</li></ul> <h2 id="api规范"><a href="#api规范" class="header-anchor">#</a> API规范</h2> <p>我们在<code>application/api/controller</code>文件夹中开发不同版本（类型）的<code>API</code>，例如项目源码中，我们在<code>application/api/controller</code>文件夹中有两个子目录<code>cms</code>和<code>v1</code> <img src="/lin-cms-book/assets/img/1.3fb8f927.png" alt="image"></p> <p>我们在对应版本（类型）的目录下去开发我们的控制器类，这样有利于我们管理和维护，说白了就是可以快速定位代码所在。</p> <blockquote><p><code>controller</code>下的目录结构不是唯一也没有标准，项目源码仅提供一个参考。</p></blockquote> <p>接下来我们尝试通过阅读<code>User</code>控制器类的源码来了解下设计思路，其余的控制器类基本大同小异，以其中<code>login</code>方法为例</p> <p><img src="/lin-cms-book/assets/img/2.2a47a06c.png" alt="image"></p> <p>在<code>User</code>类下面，我们定义了一个<code>login</code>方法，它就是一个Api，既然是Api,就必须要有一个与之对应的访问<code>url</code>，前端在访问这个<code>url</code>之后，就会<code>路由</code>到我们这个<code>login</code>方法。更多关于路由的介绍请阅读<a href="#%E8%B7%AF%E7%94%B1">路由</a>部分的内容。</p> <p>这里方法里面做了如下几件事：</p> <ol><li><p>调用了一个自定义的验证器</p></li> <li><p>获取了请求参数。</p></li> <li><p>验证用户是否存在和密码是否正确。</p></li> <li><p>生成令牌</p></li> <li><p>记录日志</p></li> <li><p>返回令牌</p></li></ol> <p>这里的代码很简单，控制器层没有做任何的逻辑判断和业务实现，交给了对应的模型或者方法去执行。控制器只负责流程的控制而不关心具体的实现。假如现在有一个需求，你要限制用户在某个时段不能登陆，那么你只需要封装好方法，然后在获取令牌之前插入一段调用方法的代码即可。控制器层不去处理逻辑判断和业务实现有助于后期的扩展和代码简洁性，在某些复杂的业务接口，如果在控制器层涉及过多的逻辑判断和业务处理必然导致后期的维护和扩展上的问题，而且也不便于理解执行流程。</p> <h2 id="数据库模型规范"><a href="#数据库模型规范" class="header-anchor">#</a> 数据库模型规范</h2> <p>ThinkPHP 框架提供了强大的<code>ORM</code>实现，通过对使用ThinkPHP内置的<code>模型</code>功能，可以让你高效、方便的操作数据库的各种操作即便你不懂<code>SQL</code>语句。基于框架的模型功能，可以轻松、灵活的定义模型之前的关联关系，如一对多，多对多，远程一对多等，同时可以实现<code>预查询</code>，解决关联查询的n+1的性能问题。</p> <p>项目已经为你打包了核心模型，他们分别是LinUser、LinAuth、LinGroup、LinLog，模型中已经包含了常用的功能，当然你可以自己创建一个模型类并继承他们来实现扩展或者重写里面的方法。</p> <p>更多关于ThinkPhP<code>ORM</code>的用法可以参考<a href="https://www.kancloud.cn/manual/thinkphp5_1/354041" target="_blank" rel="noopener noreferrer">ThinkPHP TP5.1完全开发手册——模型<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p> <h2 id="路由"><a href="#路由" class="header-anchor">#</a> 路由</h2> <p>在<code>User</code>类下面，我们定义了一个<code>login</code>方法，也就是一个Api，既然是Api,就必须要有一个与之对应的访问<code>url</code>，前端在访问这个<code>url</code>之后，就会<code>路由</code>到我们这个<code>login</code>方法。那么<code>路由</code>如何定义呢？TP5框架提供了很多种的路由定义方式，但总的来说分为<code>注解路由</code>和<code>配置路由</code>两种，从字面意思来理解也很好理解，前者就是在方法上面，像写注释一样给方法写入一段注释，框架就会解析然后定义一条路由。而后者，就是通过一个配置文件来定义路由，配置文件的路径在项目根目录下的<code>/route/route.php</code>,打开后会看到如下内容：</p> <p><img src="/lin-cms-book/assets/img/3.34c3f5b2.png" alt="image"></p> <p>打开后你会发现里面已经定义了很多路由，看不懂没关系，这里你只需要知道<code>方法需要有一个对应的路由</code>这种概念即可，这里我们为<code>login</code>方法定义了一条<code>请求类型</code>为<code>GET</code>,<code>url</code>为<code>cms/user/login</code>的路由规则，他会转发到<code>api/cms.User/login</code>。当前端发起了一个<code>http</code>请求，请求的地址是<code>cms/user/login</code>，就会走到我们User控制器类下面的login方法并执行方法内的逻辑。</p> <blockquote><p>当你在编写其他接口时候，也是按照这种套路，创建控制器类——创建控制器类下面的方法——为方法定义路由。</p></blockquote> <blockquote><p>目前项目源码中路由功能采用的是<code>配置路由</code>,在下一个版本中将会全部替换成注解路由</p></blockquote> <h2 id="验证器"><a href="#验证器" class="header-anchor">#</a> 验证器</h2> <p><code>ThinkPHP</code>框架提供了强大且灵活的验证器，除了内置的一系列验证规则之外还可以自定义验证规则，具体支持的内置验证规则可查看官方开发手册<a href="https://www.kancloud.cn/manual/thinkphp5_1/354101" target="_blank" rel="noopener noreferrer">验证器<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p> <p>我们提供了一个已经封装好的验证器基类<code>BaseValidate</code>,位于<code>vendor\lin-cms-tp5\base-core\src\validate</code></p> <p>我们推荐开发者在<code>application\api\validate</code>这个目录下去书写你的自定义验证器,且继承于<code>BaseValidate</code>基类</p> <p><img src="/lin-cms-book/assets/img/4.a0c8d5c9.png" alt="image"></p> <p>定义完验证器后，如下方式调用，如果通过则会继续执行控制器中的逻辑，否则会抛出一个异常并中断执行</p> <div class="language-php line-numbers-mode"><pre class="language-php"><code>    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">login</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
       <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">LoginForm</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">goCheck</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//调用自定义验证器</span>
        <span class="token comment">// 省略了一堆逻辑</span>
        <span class="token keyword">return</span> <span class="token variable">$result</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>异常信息</p> <div class="language-json line-numbers-mode"><pre class="language-json"><code><span class="token punctuation">{</span>
    <span class="token property">&quot;msg&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">&quot;password&quot;</span><span class="token operator">:</span> <span class="token string">&quot;密码不能为空&quot;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token property">&quot;error_code&quot;</span><span class="token operator">:</span> <span class="token number">66667</span><span class="token punctuation">,</span>
    <span class="token property">&quot;request_url&quot;</span><span class="token operator">:</span> <span class="token string">&quot;cms/user/login&quot;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><blockquote><p>在某些情况下，<code>(new LoginForm())-&gt;goCheck();</code>这种语法显得不够优雅，又或者为了一个并不需要复杂验证的接口实现一个验证器显得有些麻烦，于是LinCMS TP5 在原生框架的基础上实现了<code>注解验证器</code>和<code>注解参数验证</code></p></blockquote> <p><strong>注解验证器示例</strong></p> <p>原本<code>(new LoginForm())-&gt;goCheck()</code>的调用方式不需要了，只需要在控制器的注释内容中加入固定格式的注解<code>@validate('自定义验证器类名')</code>，即可实现调用自定义验证器。这里的<code>@validate('LoginForm')</code>相当于调用的<code>\app\api\validate\user\LoginForm</code>去验证</p> <div class="language-php line-numbers-mode"><pre class="language-php"><code>    <span class="token comment">/**
     * 账户登录
     * @param Request $request
     * @validate('LoginForm') //注解验证器
     * @return array
     * @throws \think\Exception
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">login</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token comment">// (new LoginForm())-&gt;goCheck();  # 开启注释验证器以后，本行可以去掉，这里做更替说明</span>
        <span class="token comment">// 省略了逻辑代码</span>
    <span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p><strong>注解参数验证示例</strong></p> <p>使用<code>@param('参数名','参数注释','参数规则')，进行单个参数验证</code><br>
例如：<code>@param('id','ID信息','require|max:1000|min:1')</code></p> <div class="language-php line-numbers-mode"><pre class="language-php"><code>    <span class="token comment">/**
     * 查询指定bid的图书
     * @param Request $bid
     * @param('bid','bid的图书','require')
     * @return mixed
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">getBook</span><span class="token punctuation">(</span><span class="token variable">$bid</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token variable">$result</span> <span class="token operator">=</span> BookModel<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token variable">$bid</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token variable">$result</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>这里要求调用时参数<code>bid</code>为<code>require</code>，就是必须。</p> <blockquote><p>更多详细介绍查阅<a href="https://github.com/china-wangyu/lin-cms-tp-validate-core" target="_blank" rel="noopener noreferrer">注释验证器文档<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p></blockquote> <h2 id="模型管理和权限管理"><a href="#模型管理和权限管理" class="header-anchor">#</a> 模型管理和权限管理</h2> <blockquote><p>阅读本小节前，请确保你一定完成了<a href="/lin-cms-book/start/server_deploy.html">快速开始</a>的全部内容 本小结使用<code>postman</code>作为 http 测试工具，请确保你有 postman 或类似的 http 测试工具，它是我们后续开发必不可少的工具。</p></blockquote> <h3 id="架构介绍"><a href="#架构介绍" class="header-anchor">#</a> 架构介绍</h3> <p>Lin 的定位是一整套的 ThinkPHP TP5 CMS 解决方案。对于任何的 CMS 来说，权限这一块都是<strong>不可或缺</strong>的，因此 Lin 在基础框架中便已经集成了权限模块，它是开箱即用的。</p> <p>不过 Lin 的权限模块的概念可能与其它的权限框架由些许不同，当然你完全不用担心，因为大部分权限系统的模式都大同小异。</p> <p>在 Lin 的权限模块中，我们有三个模型类来组成这个这个权限模块。如下：</p> <p>用户模型（LinUser，数据表名称为 lin_user） 用户是权限系统服务的基本单位，CMS 与一些网站的很大的区别在于，CMS 可能不存在不用登陆便可进入的页面（登陆页除外）。</p> <p>简而言之，用户是必须的。在源代码<code>model/LinUser.php</code>可以找到<code>LinUser</code>这个类。LinUser类实则是一个数据库模型类，它有一些必要的属性和实用的方法。</p> <p>权限组模型（LinGroup，数据表名称为 lin_group） 权限组是一个非常重要的概念，权限组是权限分配的基本单位，同时它也是容纳用户的容器，它是用户与权限之间的纽带。</p> <p>一个用户只能属于一个权限组，超级管理员（super）不属于任何权限组，但超级管理员拥有所有的权限，一个权限组可以拥有多个用户。</p> <p>权限组也可拥有多个权限，也就是说，在某个权限组的用户拥有该权限组的所有权限。</p> <p>如果，你还不清楚，请你在源代码<code>model/LinGroup.php</code>中阅读<code>LinGroup</code>这个类的属性。</p> <p>权限模型（auth_model，数据表名称为 lin-auth） 你可以把一个权限理解成一把钥匙，然你拥有这把钥匙的时候你就可以打开某扇门，而当你没有这把钥匙的时候，你就会被锁在门的外面。</p> <p>所以对于某个用户，比如说：你，当你拥有某个权限时，你就可以访问某个 API（或多个 API），而当你没有这个权限时，你访问 API 时会得到一个授权失败或禁止的信息。</p> <p>想了解更详细的细节，请查看源代码<code>model/LinAuth</code>中LinAuth`这个类。</p> <blockquote><p>上述中的源代码，请在框架根目录下的<code>vendor\lin-cms-tp5</code>中寻找。</p></blockquote> <h3 id="基本使用"><a href="#基本使用" class="header-anchor">#</a> 基本使用</h3> <blockquote><p>要实现权限控制，只需要在控制器方法上添加相应固定格式的注解即可实现，下面演示如何使用</p></blockquote> <ul><li>控制器方法</li></ul> <div class="language-php line-numbers-mode"><pre class="language-php"><code>    <span class="token comment">/**
     * @adminRequired
     * @permission('创建用户','管理员','hidden')
     * @param Request $request
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">register</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RegisterForm</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">goCheck</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token variable">$params</span> <span class="token operator">=</span> <span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        LinUser<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">createUser</span><span class="token punctuation">(</span><span class="token variable">$params</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token function">logger</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'创建了一个用户'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">writeJson</span><span class="token punctuation">(</span><span class="token number">201</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">''</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'用户创建成功'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br></div></div><p>这里我们创建了一个控制器方法，用来创建一个后台账户，我们需要给这个方法添加一个访问权限，权限名称叫<code>创建用户</code>,属于<code>管理员</code>权限模块，于是我们在方法的注释中加入<code>@permission('创建用户','管理员','hidden')</code>这一段内容，这样我就为这个方法标识好权限了，这里<code>hidden</code>的作用就是让这个权限信息不会出现在前端查询所有可分配权限时出现，示例代码中我们将所有管理员模块的权限都添加了<code>hidden</code>。当你不想一个权限能够被前端所展示并分配给某个分组的时候，可以尝试配置这个选项。</p> <p>这里我们还会发现，我们除了使用<code>@permission</code>注解来描述一个权限的信息以外，我们这里还使用了一个<code>@adminRequired</code>,这个注解的作用是用来描述权限等级的，在lin-cms-tp5中，权限等级有三种：</p> <blockquote><ol><li>@adminRequired 只允许超级管理员访问(root分组下的用户)</li> <li>@groupRequired 需具备@permission中定义的分组权限才可访问</li> <li>@loginRequired 登陆即可访问</li></ol></blockquote> <p>从上面的描述可以得知，<code>@adminRequired</code>这个注解是最严格的权限控制，限制只有root分组下的用户才能调用该接口，即便你给某个其他分组分配了<code>创建用户</code>这个权限，调用的时候依然会提示权限不足。同样的，如果你给一个接口加上了<code>@loginRequired</code>注解，同时又配置了<code>@permission</code>注解，那么也是没有意义的，因为权限控制的中间件识别到这个接口只是需要登录即可，只要当前的登陆状态还是有效的就会被允许，不会再去判断当前用户是否具有<code>@permission</code>中标识的权限。</p> <p>通过三个等级的权限级别注解和@permission注解，开发者可以灵活组合配置来实现权限控制需求。</p> <ul><li>权限校验中间件</li></ul> <p>中间件文件所在路径<code>/application/http/middleware/Authentication.php</code>，可阅读中间件源码查看实现逻辑和流程；该中间件在项目根目录<code>/route/route.php</code>中进行了挂载。</p> <div class="language-php line-numbers-mode"><pre class="language-php"><code>
Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">group</span><span class="token punctuation">(</span><span class="token single-quoted-string string">''</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">group</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'cms'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 省略代码</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

    Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">group</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'v1'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 省略代码</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">middleware</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'Authentication'</span><span class="token punctuation">,</span><span class="token single-quoted-string string">'ReflexValidate'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">allowCrossDomain</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token shell-comment comment">#挂载权限校验中间件auth到路由分组的根节点,根节点下的嵌套分组都会应用。</span>

</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><blockquote><p>权限的名称和所属模块是可以自由定义的，但要注意因为lin-cms是前后端分离的项目，你在后端为控制器方法定义的权限内容必须是和你前端定义的权限名称相匹配这样菜单才会正常渲染。另外，管理员模块即便不添加hidden并分配给了分组，前端也不会显示对应菜单。</p></blockquote> <h2 id="行为日志"><a href="#行为日志" class="header-anchor">#</a> 行为日志</h2> <p>LinCMS TP5 通过原生框架的Hook实现了行为日志的记录，在需要记录日志的地方监听logger钩子，即可实现日志记录</p> <div class="language-php line-numbers-mode"><pre class="language-php"><code><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">register</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token variable">$params</span> <span class="token operator">=</span> <span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        LinUser<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">createUser</span><span class="token punctuation">(</span><span class="token variable">$params</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Hook<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'logger'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'创建了一个用户'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">writeJson</span><span class="token punctuation">(</span><span class="token number">201</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">''</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'用户创建成功'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p><code>Hook::listen()</code>接收两个参数,第一个是钩子名称，第二个参数内容。这里我们调用了logger这个钩子，钩子的具体行为我们定义在<code>application\api\behavior</code>目录下的<code>Logger.php</code>文件中，文件内实现了一个<code>run</code>方法</p> <div class="language-php line-numbers-mode"><pre class="language-php"><code>    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token variable">$params</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>

        <span class="token comment">// 行为逻辑</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">empty</span><span class="token punctuation">(</span><span class="token variable">$params</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">LoggerException</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
                <span class="token single-quoted-string string">'msg'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token single-quoted-string string">'日志信息不能为空'</span>
            <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">is_array</span><span class="token punctuation">(</span><span class="token variable">$params</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">list</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'uid'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$uid</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'nickname'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$nickname</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'msg'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$message</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token variable">$params</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
            <span class="token variable">$uid</span> <span class="token operator">=</span> Token<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">getCurrentUID</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token variable">$nickname</span> <span class="token operator">=</span> Token<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">getCurrentName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token variable">$message</span> <span class="token operator">=</span> <span class="token variable">$params</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token variable">$data</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
            <span class="token single-quoted-string string">'message'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$nickname</span> <span class="token punctuation">.</span> <span class="token variable">$message</span><span class="token punctuation">,</span>
            <span class="token single-quoted-string string">'user_id'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$uid</span><span class="token punctuation">,</span>
            <span class="token single-quoted-string string">'user_name'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token variable">$nickname</span><span class="token punctuation">,</span>
            <span class="token single-quoted-string string">'status_code'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> Response<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token single-quoted-string string">'method'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> Request<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">method</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token single-quoted-string string">'path'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> Request<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">path</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token single-quoted-string string">'authority'</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token single-quoted-string string">''</span>
        <span class="token punctuation">]</span><span class="token punctuation">;</span>
        LinLog<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token variable">$data</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br></div></div><p><code>run</code>方法主要是实现了字段信息的赋值和拼接，可根据实际情况调整。</p> <h2 id="配置"><a href="#配置" class="header-anchor">#</a> 配置</h2> <p>本项目遵循<code>ThinkPHP</code>的框架原则，即惯例重于配置的原则，系统会按照下面的顺序来加载配置文件（配置的优先顺序从右到左）。</p> <blockquote><p>惯例配置-&gt;应用配置-&gt;模块配置-&gt;动态配置</p></blockquote> <ul><li>惯例配置：核心框架内置的配置文件，无需更改。</li> <li>应用配置：每个应用的全局配置文件（框架安装后会生成初始的应用配置文件），有部分配置参数仅能在应用配置文件中设置。</li> <li>模块配置：每个模块的配置文件（相同的配置参数会覆盖应用配置），有部分配置参数模块配置是无效的，因为已经使用过。</li> <li>动态配置：主要是指在控制器或者行为中进行（动态）更改配置，该配置方式只在当次请求有效，因为不会保存到配置文件中</li></ul> <h3 id="配置文件和目录"><a href="#配置文件和目录" class="header-anchor">#</a> 配置文件和目录</h3> <p>为更好的应对模块化的开发要求，<code>ThinkPHP5.1</code>的应用配置主要包括应用配置目录和模块配置目录，结构如下：</p> <div class="language-bash line-numbers-mode"><pre class="language-bash"><code>├─config（应用配置目录）
│  ├─app.php            应用配置
│  ├─cache.php          缓存配置
│  ├─cookie.php         Cookie配置
│  ├─database.php       数据库配置
│  ├─log.php            日志配置
│  ├─session.php        Session配置
│  ├─template.php       模板引擎配置
│  ├─trace.php          Trace配置
│  └─ <span class="token punctuation">..</span>.               更多配置文件
│  
├─route（路由目录）
│  ├─route.php          路由定义文件
│  └─ <span class="token punctuation">..</span>.               更多路由定义文件
│  
├─application （应用目录）
│  └─module （模块目录）
│     └─config（模块配置目录）
│        ├─app.php            应用配置
│        ├─cache.php          缓存配置
│        ├─cookie.php         Cookie配置
│        ├─database.php       数据库配置
│        ├─log.php            日志配置
│        ├─session.php        Session配置
│        ├─template.php       模板引擎配置
│        ├─trace.php          Trace配置
│        └─ <span class="token punctuation">..</span>.               更多配置文件
│
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br></div></div><p>了解更多关于<a href="https://www.kancloud.cn/manual/thinkphp5_1/353951" target="_blank" rel="noopener noreferrer">ThinkPHP5.1 配置<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p> <blockquote><p>ThinkPHP默认使用的是.php文件的形式来管理配置文件，这种方式存在一定的安全问题和性能问题，这里推荐你在任何PHP项目中都使用Yaconf来管理你的配置文件。</p></blockquote> <blockquote><p>关于<code>Yaconf</code>的安装和配置用法可以<a href="http://www.laruence.com/2015/06/12/3051.html" target="_blank" rel="noopener noreferrer">参考这里<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p></blockquote> <h2 id="令牌"><a href="#令牌" class="header-anchor">#</a> 令牌</h2> <h3 id="为何选择jwt"><a href="#为何选择jwt" class="header-anchor">#</a> 为何选择jwt</h3> <p>最近几年 Restful API 及 SPA(单页面应用) 的盛行，cookie-session 的机制似乎越来越不适合前后端分离的场景。 在分布式和微服务的趋势下，不少人选择在 redis 中存储 session 来达到单点登陆的效果，这无疑增加了成本和开发难度。 于是更多的人转而使用 jwt 来管理用户会话和授权，在 jwt<a href="https://jwt.io/introduction/" target="_blank" rel="noopener noreferrer">官网<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>介绍其两大使用场景。</p> <p>Authorization(授权)：这是 jwt 应用最为广泛的场景。jwt 将数据加密存储，分发给前端，前端将其放在特定的 header 字段 中（也有放在 params 和 body 中），服务器收到请求后，解析 jwt 判断用户身份，对用户请求进行限权。</p> <p>Information Exchange(数据交换): jwt 可以通过公钥和私钥对信息进行加密，双方通信后，互得数据。</p> <p>关于 jwt 的使用及生态请阅读 jwt<a href="https://jwt.io/introduction/" target="_blank" rel="noopener noreferrer">官网<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>获得更多信息。</p> <h3 id="access-token-和-refresh-token"><a href="#access-token-和-refresh-token" class="header-anchor">#</a> access_token 和 refresh_token</h3> <p>在一般 jwt 应用中，access_token和refresh_token是一对相互帮助的好搭档，前面讲到用户在前端登陆后，服务器会发送 access_token 和 refresh_token 给前端，前端在得到这两个 token 之后必须<strong>谨慎存储</strong>。</p> <p>access_token ：用来用户鉴权，控制用户对接口，资源的访问。access_token 十分重要，它是服务器对前端有力控制的唯一途径，故一般其生存周期十分短，一般在 2 个小时左右，更有甚者，其生命周期只有 15 分钟，在 Lin 中默认 1 个小时。</p> <p>refresh_token ：用户通过登陆后获得 access_token，而 access_token 的生命周期十分短暂，但是用户登陆太频繁会严重影响体验，因此需要一种免登陆便能获取 access_token 的方式。refresh_token 主要用来解决该问题，refresh_token 的生命周期较长，一般为 30 天左右，但 refresh_token 不能被用来用户身份鉴权和获取资源，它只能被用来重新获取 access_token。当前端发现 access_token 过期时，便应通过 refresh_token 重新获取 access_token。</p> <h3 id="项目中的使用"><a href="#项目中的使用" class="header-anchor">#</a> 项目中的使用</h3> <p>在 Lin 中已经默认集成了 jwt 的机制，你可以通过 HTTP 请求来进行相关的操作。</p> <h3 id="用户获取-access-token-和-refresh-token"><a href="#用户获取-access-token-和-refresh-token" class="header-anchor">#</a> 用户获取 access_token 和 refresh_token</h3> <p><code>path</code>: /cms/user/login</p> <p><code>method</code>: post</p> <p><code>参数</code>:</p> <table><thead><tr><th>name</th> <th>说明</th> <th>类型</th> <th>作用</th></tr></thead> <tbody><tr><td>nickname</td> <td>用户名</td> <td>string</td> <td></td></tr> <tr><td>password</td> <td>密码</td> <td>string</td> <td></td></tr></tbody></table> <p><code>结果</code>:</p> <div class="language-json line-numbers-mode"><pre class="language-json"><code><span class="token punctuation">{</span>
  <span class="token property">&quot;access_token&quot;</span><span class="token operator">:</span> <span class="token string">&quot;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MzU1MzMyNjMsIm5iZiI6MTUzNTUzMzI2MywianRpIjoiMTlkZWUwNzQtNzUxYi00MjBlLTk3NjAtZDRkMzc3YjdjMjUyIiwiZXhwIjoxNTM1NjE5NjYzLCJpZGVudGl0eSI6InBlZHJvIiwiZnJlc2giOmZhbHNlLCJ0eXBlIjoiYWNjZXNzIn0.9sNmAV5anxY5N1S1kaXzRRpdjzVX3fX6iI0ZjxGiiVs&quot;</span><span class="token punctuation">,</span>
  <span class="token property">&quot;refresh_token&quot;</span><span class="token operator">:</span> <span class="token string">&quot;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MzU1MzMyNjMsIm5iZiI6MTUzNTUzMzI2MywianRpIjoiYjU0OWIwZGEtMTE3MS00NzJlLWE0MDMtMDFkMGRkZTRjOTYzIiwiZXhwIjoxNTM4MTI1MjYzLCJpZGVudGl0eSI6InBlZHJvIiwidHlwZSI6InJlZnJlc2gifQ.cBnqEBnome-dMFEueQ8oCJfoXX9_mzQJAGjyeq4bYh8&quot;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p><code>使用</code>:</p> <p>在请求其他资源或接口时在 header 中，加入<code>Authorization</code>字段，字段值为<code>Bearer</code>加上<code>access_token</code>，注意两个字段中间必须有一个空格，如下：</p> <div class="language-bash line-numbers-mode"><pre class="language-bash"><code>Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MzU1MzMyNjMsIm5iZiI6MTUzNTUzMzI2MywianRpIjoiMTlkZWUwNzQtNzUxYi00MjBlLTk3NjAtZDRkMzc3YjdjMjUyIiwiZXhwIjoxNTM1NjE5NjYzLCJpZGVudGl0eSI6InBlZHJvIiwiZnJlc2giOmZhbHNlLCJ0eXBlIjoiYWNjZXNzIn0.9sNmAV5anxY5N1S1kaXzRRpdjzVX3fX6iI0ZjxGiiVs
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>服务器会解析该字段并得到用户信息，对用户进行鉴权。</p> <h3 id="refresh-token-获取-access-token"><a href="#refresh-token-获取-access-token" class="header-anchor">#</a> refresh_token 获取 access_token</h3> <p><code>path</code>: /cms/user/refresh</p> <p><code>method</code>: get</p> <p><code>参数</code>: 无，注意在 Authorization 中加上 refresh_token。 <code>结果</code>:</p> <div class="language-json line-numbers-mode"><pre class="language-json"><code><span class="token punctuation">{</span>
  <span class="token property">&quot;access_token&quot;</span><span class="token operator">:</span>
    <span class="token string">&quot;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MzU1MzMyNjMsIm5iZiI6MTUzNTUzMzI2MywianRpIjoiMTlkZWUwNzQtNzUxYi00MjBlLTk3NjAtZDRkMzc3YjdjMjUyIiwiZXhwIjoxNTM1NjE5NjYzLCJpZGVudGl0eSI6InBlZHJvIiwiZnJlc2giOmZhbHNlLCJ0eXBlIjoiYWNjZXNzIn0.9sNmAV5anxY5N1S1kaXzRRpdjzVX3fX6iI0ZjxGiiVs&quot;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p><code>使用</code>：</p> <p>用户获取资源时，<code>Authorization</code>字段的值为<code>Bearer</code>加上<code>access_token</code>，当通过 refresh_token 获取 access_token 时，应将<code>Authorization</code>中的 access_token 替换为 refresh_token，如：</p> <div class="language-bash line-numbers-mode"><pre class="language-bash"><code>Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MzU1MzMyNjMsIm5iZiI6MTUzNTUzMzI2MywianRpIjoiYjU0OWIwZGEtMTE3MS00NzJlLWE0MDMtMDFkMGRkZTRjOTYzIiwiZXhwIjoxNTM4MTI1MjYzLCJpZGVudGl0eSI6InBlZHJvIiwidHlwZSI6InJlZnJlc2gifQ.cBnqEBnome-dMFEueQ8oCJfoXX9_mzQJAGjyeq4bYh8
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>服务器会解析该字段，如 refresh_token 字段未过期则会发送新的 access_token。</p> <blockquote><p>TIP<br>
当然在 Lin 的前端框架中，已经默认实现了以上机制，你大可不必自己去实现，当然如果你想深入了解，也可在阅读本小节后自行尝试。</p></blockquote></div> <footer class="page-edit"><!----> <div class="last-updated"><span class="prefix">最后更新时间:</span> <span class="time">10/7/2020, 11:56:44 PM</span></div></footer> <!----> </main></div><div class="global-ui"></div></div>
    <script src="/lin-cms-book/assets/js/app.43f3ff7b.js" defer></script><script src="/lin-cms-book/assets/js/2.1c264783.js" defer></script><script src="/lin-cms-book/assets/js/3.12364cb0.js" defer></script>
  </body>
</html>
